home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
jade
/
lisp
/
edit.jl
< prev
next >
Wrap
Lisp/Scheme
|
1995-03-09
|
15KB
|
514 lines
;;;; edit.jl -- High-level editing functions
;;; Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
;;; This file is part of Jade.
;;; Jade is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.
;;; Jade is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;; You should have received a copy of the GNU General Public License
;;; along with Jade; see the file COPYING. If not, write to
;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
(defvar word-regexp "[a-zA-Z0-9]"
"Regular expression which defines a character in a word.")
(defvar word-not-regexp "[^a-zA-Z0-9]|$"
"Regular expression which defines anything that is not in a word.")
(defvar paragraph-regexp "^[\t ]*$"
"Regular expression which matches a paragraph-separating piece of text.")
(make-variable-buffer-local 'word-regexp)
(make-variable-buffer-local 'word-not-regexp)
(make-variable-buffer-local 'paragraph-regexp)
(defvar auto-mark (make-mark)
"Mark which some commands use to track the previous cursor position.")
;; Words
(defun forward-word (&optional number pos move)
"Return the position of first character after the end of this word.
NUMBER is the number of words to move, negative values mean go backwards.
If MOVE is t then the cursor is moved to the result."
(interactive "p\n\nt")
(unless number
(setq number 1))
(unless pos
(setq pos (cursor-pos)))
(cond
((< number 0)
;; go backwards
(while (/= number 0)
(setq pos (prev-char 1 pos))
(when (looking-at word-not-regexp pos)
;; not in word
(unless (setq pos (find-prev-regexp word-regexp pos))
(error "Start of buffer")))
;; in middle of word
(unless (setq pos (find-prev-regexp word-not-regexp pos))
(error "Start of buffer"))
(setq
pos (find-next-regexp word-regexp pos)
number (1+ number))))
(t
;; forwards
(while (/= number 0)
(when (looking-at word-not-regexp pos)
;; already at end of a word
(unless (setq pos (find-next-regexp word-regexp pos))
(error "End of buffer")))
(unless (setq pos (find-next-regexp word-not-regexp pos))
(error "End of buffer"))
(setq number (1- number)))))
(when move
(goto-char pos))
pos)
(defun backward-word (&optional number pos move)
"Basically `(forward-word -NUMBER POS MOVE)'"
(interactive "p\n\nt")
(forward-word (if number (- number) -1) pos move))
(defun kill-word (count)
"Kills from the cursor to the end of the word."
(interactive "p")
(kill-area (cursor-pos) (forward-word count)))
(defun backward-kill-word (count)
"Kills from the start of the word to the cursor."
(interactive "p")
(kill-area (forward-word (- count)) (cursor-pos)))
(defun word-start (&optional pos)
"Returns the position of the start of *this* word."
(when (looking-at word-regexp pos)
(unless (find-prev-regexp word-not-regexp pos)
(error "Start of buffer"))
(find-next-regexp word-regexp (match-end))))
(defun in-word-p (&optional pos)
"Returns t if POS is inside a word."
(when (looking-at word-regexp pos)
t))
(defun mark-word (count &optional pos)
"Marks COUNT words from POS."
(interactive "p")
(set-rect-blocks nil nil)
(mark-block (or pos (cursor-pos)) (forward-word count pos)))
(defun transpose-words (count)
"Move the word at (before) the cursor COUNT words forwards."
(interactive "p")
(transpose-items 'forward-word 'backward-word count))
;; Paragraphs
(defun forward-paragraph (&optional pos buf move)
"Returns the position of the start of the next paragraph. If MOVE
is t then the cursor is set to this position."
(interactive "\n\nt")
(setq pos (or (find-next-regexp paragraph-regexp
(next-char 1 (if pos
(copy-pos pos)
(cursor-pos)))
buf)
(buffer-end)))
(when move
(goto-char pos))
pos)
(defun backward-paragraph (&optional pos buf move)
"Returns the start of the previous paragraph. If MOVE is t the cursor is
set to this position."
(interactive "\n\nt")
(setq pos (or (find-prev-regexp paragraph-regexp
(prev-char 1 (if pos
(copy-pos pos)
(cursor-pos)))
buf)
(buffer-start)))
(when move
(goto-char pos))
pos)
(defun mark-paragraph ()
"Set the block-marks to the current paragraph."
(interactive)
(let
((par (forward-paragraph)))
(set-rect-blocks nil nil)
(mark-block (backward-paragraph par) par)))
;; Block handling
(defun copy-block (&aux rc)
"If a block is marked in the current window, return the text it contains and
unmark the block."
(when (blockp)
(setq rc (funcall (if (rect-blocks-p) 'copy-rect 'copy-area)
(block-start) (block-end)))
(block-kill))
rc)
(defun cut-block (&aux rc)
"Similar to `copy-block' except the block is cut (copied then deleted) from
the buffer."
(when (blockp)
(setq rc (funcall (if (rect-blocks-p) 'cut-rect 'cut-area)
(block-start) (block-end)))
(block-kill))
rc)
(defun delete-block ()
"Deletes the block marked in the current window (if one exists)."
(interactive)
(when (blockp)
(funcall (if (rect-blocks-p) 'delete-rect 'delete-area)
(block-start) (block-end))
(block-kill)))
(defun insert-block (&optional pos)
"If a block is marked in the current window, copy it to position POS, then
unmark the block."
(interactive)
(when (blockp)
(if (rect-blocks-p)
(insert-rect (copy-rect (block-start) (block-end)) pos)
(insert (copy-area (block-start) (block-end)) pos))
(block-kill)))
(defun toggle-rect-blocks ()
"Toggles the state of the flag saying whether blocks in this window are
marked sequentially (the default) or as rectangles."
(interactive)
(set-rect-blocks nil (not (rect-blocks-p))))
(defun kill-block ()
"Kills the block marked in this window."
(interactive)
(kill-string (cut-block)))
(defun copy-block-as-kill ()
"Kills the block marked in this window but doesn't actually delete it from
the buffer."
(interactive)
(kill-string (copy-block)))
(defun mark-block (start end)
"Mark a block from START to END. This does an extra redraw if there's already
a block marked to save lots of flicker."
(if (blockp)
(progn
(block-kill)
;; Cunning hack -- the refresh algorithm(?) doesn't like the block
;; killed then reset in one go, the whole screen is redraw :-( So
;; do two refreshes...
(refresh-all))
(block-kill))
(block-start start)
(block-end end))
(defun mark-whole-buffer ()
"Mark a block containing the whole of the buffer."
(interactive)
(set-rect-blocks nil nil)
(mark-block (buffer-start) (buffer-end)))
(defun upcase-area (start end &optional buffer)
"Makes all alpha characters in the specified region of text upper-case."
(interactive "-m\nM")
(translate-area start end upcase-table buffer))
(defun downcase-area (start end &optional buffer)
"Makes all alpha characters in the specified region of text lower-case."
(interactive "-m\nM")
(translate-area start end downcase-table buffer))
(defun upcase-word (count)
"Makes the next COUNT words from the cursor upper-case."
(interactive "p")
(let
((pos (forward-word count)))
(upcase-area (cursor-pos) pos)
(goto-char pos)))
(defun capitalize-word ()
"The first character of this word (the one under the cursor) is made
upper-case, the rest lower-case."
(interactive)
(unless (in-word-p)
(goto-char (find-next-regexp word-regexp)))
(translate-area (cursor-pos) (next-char) upcase-table)
(goto-next-char)
(when (in-word-p)
(downcase-word 1)))
(defun downcase-word (count)
"Makes the word under the cursor lower case."
(interactive "p")
(let
((pos (forward-word count)))
(downcase-area (cursor-pos) pos)
(goto-char pos)))
(defun mark-region ()
"Sets the block-marks to the area between the cursor position and the
auto-mark"
(interactive)
(block-kill)
(when (eq (mark-file auto-mark) (current-buffer))
(let
((curs (cursor-pos)))
(cond
((> curs (mark-pos auto-mark))
(mark-block (mark-pos auto-mark) curs))
(t
(mark-block curs (mark-pos auto-mark)))))))
;; Killing
;; Sometime I'll remove the dependancy on the *-clip functions, the killed
;; text is only accessed via these functions so it should be easy...
(defun kill-string (string)
"Adds STRING to the kill storage. If the last command also kill'ed something
the string is appended to."
(write-clip 0 (if (eq last-command 'kill)
(concat (killed-string) string)
string))
;; this command did some killing
(setq this-command 'kill)
string)
(defun killed-string (&optional depth)
"Returns the string in the kill-buffer at position DEPTH. Currently only one
string is stored so DEPTH must be zero or not specified."
(if (or (null depth) (= depth 0))
(read-clip 0)
(error "No string at specified depth in kill storage" depth)))
(defun kill-area (start end)
"Kills a region of text in the current buffer from START to END."
(interactive "-m\nM")
(kill-string (cut-area start end)))
(defun copy-area-as-kill (start end)
"Copies a region of text in the current buffer (from START to END) to the
kill storage."
(interactive "-m\nM")
(kill-string (copy-area start end)))
(defun kill-line (&optional arg)
"If the cursor is not at the end of the line kill the text from the cursor
to the end of the line, else kill from the end of the line to the start of
the next line."
(interactive "P")
(let
((count (prefix-numeric-argument arg))
(start (cursor-pos))
end)
(cond
((null arg)
(setq end (if (>= start (line-end))
(line-start (next-line))
(line-end))))
((> count 0)
(setq end (line-start (next-line count))))
(t
(setq end start
start (line-start (next-line count)))))
(kill-area start end)))
(defun kill-whole-line (count)
"Kill the whole of the current line."
(interactive "p")
(kill-area (line-start) (line-start (next-line count))))
(defun backward-kill-line ()
"Kill from the cursor to the start of the line."
(interactive)
(kill-area (if (zerop (pos-col (cursor-pos)))
(prev-char)
(line-start))
(cursor-pos)))
;; Yank
(defun yank (&optional dont-yank-block)
"Inserts text before the cursor. If a block is marked in the current buffer
and DONT-YANK-BLOCK is nil insert the text in the block. Else yank the last
killed text."
(interactive "P")
(insert (if (and (null dont-yank-block) (blockp))
(copy-block)
(killed-string))))
(defun yank-rectangle (&optional dont-yank-block)
"Similar to `yank' except that the inserted text is treated as a rectangle."
(interactive "P")
(insert-rect (if (and (null dont-yank-block) (blockp))
(copy-block)
(killed-string))))
(defun yank-to-mouse ()
"Does a `(yank)' inserting at the current position of the mouse cursor. The
cursor is left at the end of the inserted text."
(interactive)
(goto-char (mouse-pos))
(yank))
(defun transpose-items (forward-item backward-item count)
"Transpose the areas defined by the functions FORWARD-ITEM and BACKWARD-
ITEM (in the style of `forward-word', `backward-word' etc).
COUNT is the number of items to drag the item at the cursor past.\n
What actually happens is that the item before the cursor is dragged forward
over the COUNT following items."
(let
(start1 start2 end1 end2)
(while (> count 0)
;; go forwards
(setq start1 (funcall backward-item 1)
end1 (funcall forward-item 1 (copy-pos start1))
end2 (funcall forward-item 1 (copy-pos end1))
start2 (funcall backward-item 1 (copy-pos end2)))
(transpose-1)
(setq count (1- count)))
(while (< count 0)
;; go backwards
(setq start1 (funcall backward-item 1)
end1 (funcall forward-item 1 (copy-pos start1))
start2 (funcall backward-item 1 (copy-pos start1))
end2 (funcall forward-item 1 (copy-pos start2)))
(transpose-1)
(setq count (1+ count)))))
(defun transpose-1 ()
(let
(text1 text2)
(if (< start2 start1)
(progn
(setq text1 (cut-area start1 end1)
text2 (copy-area start2 end2))
(insert text2 start1)
(delete-area start2 end2)
(goto-char (insert text1 start2)))
(setq text1 (copy-area start1 end1)
text2 (cut-area start2 end2))
(goto-char (insert text1 start2))
(delete-area start1 end1)
(insert text2 start1))))
(defun abort-recursive-edit (&optional ret-val)
"Exits the innermost recursive edit with a value of VALUE (or nil)."
(interactive)
(throw 'exit ret-val))
(defun top-level ()
"Abort all recursive-edits."
(interactive)
(throw 'top-level nil))
;; Overwrite mode
(defvar overwrite-mode-p nil
"Non-nil when overwrite-mode is enabled.")
(make-variable-buffer-local 'overwrite-mode-p)
(defun overwrite-mode ()
"Minor mode to toggle overwrite."
(interactive)
(if overwrite-mode-p
(progn
(setq overwrite-mode-p nil)
(remove-minor-mode 'overwrite-mode "Overwrite")
(remove-hook 'unbound-key-hook 'overwrite-insert))
(add-minor-mode 'overwrite-mode "Overwrite")
(setq overwrite-mode-p t)
(add-hook 'unbound-key-hook 'overwrite-insert)))
(defun overwrite-insert (&optional str)
(unless str
(setq str (current-event-string)))
(when str
(setq len (length str))
(delete-area (cursor-pos) (right-char len))
(insert str)))
;; Miscellaneous editing commands
(defun backspace-char (count)
"Delete COUNT characters preceding the cursor, if the cursor is past the
end of the line simply move COUNT characters to the left."
(interactive "p")
(let
((start (prev-char count)))
(if (> (cursor-pos) (line-end))
(if (> start (line-end))
(goto-char start)
(goto-line-end)
(delete-area start (cursor-pos)))
(delete-area start (cursor-pos)))))
(defun delete-char (count)
"Delete the character under the cursor."
(interactive "p")
(delete-area (cursor-pos) (next-char count)))
(defun tab-with-spaces ()
"Insert enough spaces before the cursor to move it to the next tab position."
(interactive)
(indent-to (pos-col (next-tab)) t))
(defun just-spaces (count)
"Ensure that there are only COUNT spaces around the cursor."
(interactive "p")
(when (member (get-char) '(?\ ?\t))
(let
((pos (find-prev-regexp "[^\t ]|^")))
(when pos
(next-char 1 pos)
(when (and pos (looking-at "[\t ]+" pos))
(delete-area (match-start) (match-end))
(goto-char (match-start))))))
(unless (zerop count)
(insert (make-string count ?\ ))))
(defun no-spaces ()
"Delete all space and tab characters surrounding the cursor."
(interactive)
(just-spaces 0))
(defun open-line (count)
"Break the current line creating COUNT new lines, leaving the cursor in
its original position."
(interactive "p")
(let
((opos (cursor-pos)))
(insert (make-string count ?\n))
(goto-char opos)))
(defun transpose-chars (count)
"Move the character before the cursor COUNT characters forwards."
(interactive "p")
(transpose-items 'next-char 'prev-char count))